package com.example.activiti7.domain.service.impl;

import com.example.activiti7.domain.model.req.QueryHisTaskListReq;
import com.example.activiti7.domain.model.req.QueryTaskListReq;
import com.example.activiti7.domain.model.req.TaskBaseReq;
import com.example.activiti7.domain.model.req.TaskReq;
import com.example.activiti7.domain.model.resp.Page;
import com.example.activiti7.domain.model.resp.Response;
import com.example.activiti7.domain.model.vo.HistoricTaskVO;
import com.example.activiti7.domain.model.vo.TaskVO;
import com.example.activiti7.domain.service.ProcessTaskService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class ProcessTaskServiceImpl implements ProcessTaskService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessTaskServiceImpl.class);

    private final TaskService taskService;
    private final HistoryService historyService;
    private final RepositoryService repositoryService;
    private final RuntimeService runtimeService;

    public ProcessTaskServiceImpl(TaskService taskService, HistoryService historyService, RepositoryService repositoryService, RuntimeService runtimeService) {
        this.taskService = taskService;
        this.historyService = historyService;
        this.repositoryService = repositoryService;
        this.runtimeService = runtimeService;
    }

    @Override
    public Response<Page<TaskVO, QueryTaskListReq>> queryTodoTasks(QueryTaskListReq req) {
        TaskQuery taskQuery = taskService.createTaskQuery()
                .taskCandidateOrAssigned(req.getUserId()).active()
                .orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc();

        List<Task> tasks = taskQuery.listPage((req.getPageIndex() - 1) * req.getPageSize(), req.getPageSize());
        List<TaskVO> list = new ArrayList<>(tasks.size());
        tasks.forEach(item -> {
            TaskVO taskVO = new TaskVO();
            BeanUtils.copyProperties(item, taskVO);
            list.add(taskVO);
        });
        return Response.success(Page.builder(list, taskQuery.count(), req), "待办任务查询成功");
    }

    public Response<Page<HistoricTaskVO, QueryTaskListReq>> queryDoneTasks(QueryTaskListReq req) {
        HistoricTaskInstanceQuery hisTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery().taskCandidateUser(req.getUserId())
                .orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc();
        List<HistoricTaskInstance> historicTaskInstances = hisTaskInstanceQuery.listPage((req.getPageIndex() - 1) * req.getPageSize(), req.getPageSize());
        List<HistoricTaskVO> list = new ArrayList<>(historicTaskInstances.size());
        historicTaskInstances.forEach(item -> {
            HistoricTaskVO historicTaskVO = new HistoricTaskVO();
            BeanUtils.copyProperties(item, historicTaskVO);
            list.add(historicTaskVO);
        });
        return Response.success(Page.builder(list, hisTaskInstanceQuery.count(), req), "已办任务查询成功");
    }

    @Override
    public Response<Page<HistoricTaskVO, QueryHisTaskListReq>> queryHisTasks(QueryHisTaskListReq req) {
        // 查询指定历史流程实例所对应的所有任务
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(req.getProcessInstanceId())
                .orderByHistoricTaskInstanceStartTime()
                .asc();

        List<HistoricTaskInstance> historicTaskInstanceList = historicTaskInstanceQuery.listPage((req.getPageIndex() - 1) * req.getPageSize(), req.getPageSize());
        List<HistoricTaskVO> list = new ArrayList<>(historicTaskInstanceList.size());
        historicTaskInstanceList.stream().forEach(hisTaskInst -> {
            HistoricTaskVO historicTaskVO = new HistoricTaskVO();
            BeanUtils.copyProperties(hisTaskInst, historicTaskVO);
            Comment taskComment = taskService.getTaskComments(hisTaskInst.getId()).stream().findFirst().orElse(null);
            if (taskComment != null) {
                historicTaskVO.setComment(taskComment.getFullMessage());
            }
            list.add(historicTaskVO);
        });

        return Response.success(Page.builder(list, historicTaskInstanceQuery.count(), req), "历史任务列表查询成功");
    }

    @Override
    public Response<Boolean> completeTask(TaskReq req) {
        if (req == null) {
            return Response.error("请求对象不能为空");
        }
        if (StringUtils.isBlank(req.getTaskId())) {
            return Response.error("请求参数taskId不能为空");
        }
        if (StringUtils.isBlank(req.getUserId())) {
            return Response.error("任务处理人不能为空");
        }
        if (StringUtils.isBlank(req.getCandidate())) {
            return Response.error("任务处侯选人不能为空");
        }

        //查询任务
        TaskQuery taskQuery = taskService.createTaskQuery().active().taskId(req.getTaskId());
        if (Objects.isNull(taskQuery.singleResult())) {
            return Response.error("根据请求的任务ID未查询到任务信息");
        }

        if (Objects.isNull(taskQuery.taskCandidateOrAssigned(req.getCandidate()).singleResult())) {
            return Response.error("当前任务用户" + req.getUserId() + "无权操作");
        }

        Task task = taskQuery.singleResult();
        if (StringUtils.isNotBlank(req.getComment())) {
            taskService.addComment(req.getTaskId(), task.getProcessInstanceId(),
                    req.getComment());
        }

        LOGGER.info("> 用户{}申领待办事项: {}", req.getUserId(), task.getName());
        taskService.claim(task.getId(), req.getUserId());

        LOGGER.info("> 完成待办事项: {}", task.getName());
        taskService.complete(req.getTaskId(), req.getVariables(), true);
        return Response.success(true, "审批任务成功");
    }

    @Override
    public Response<Boolean> turnTask(TaskReq req) {
        return null;
    }

    @Override
    public Response<Boolean> rejectTask(TaskReq req) {
        if (req == null) {
            return Response.error("请求对象不能为空");
        }
        if (StringUtils.isBlank(req.getTaskId())) {
            return Response.error("请求参数taskId不能为空");
        }
        if (StringUtils.isBlank(req.getUserId())) {
            return Response.error("任务处理人不能为空");
        }

        //查询任务
        TaskQuery taskQuery = taskService.createTaskQuery().active().taskId(req.getTaskId());
        if (Objects.isNull(taskQuery.singleResult())) {
            return Response.error("根据请求的任务ID未查询到任务信息");
        }

        if (Objects.isNull(taskQuery.taskCandidateOrAssigned(req.getCandidate()).singleResult())) {
            return Response.error("当前任务用户" + req.getUserId() + "无权操作");
        }

        Task task = taskQuery.singleResult();

        return backToPrevTask(task, req.getComment());
    }

    /**
     * 撤回任务
     *
     * @param req
     * @return
     */
    public Response<Boolean> undoSubmitTask(TaskReq req) {
        if (req == null) {
            return Response.error("请求对象不能为空");
        }
        if (StringUtils.isBlank(req.getTaskId())) {
            return Response.error("请求参数taskId不能为空");
        }
        if (StringUtils.isBlank(req.getUserId())) {
            return Response.error("任务处理人不能为空");
        }

        //查询任务
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(req.getTaskId()).singleResult();
        if (Objects.isNull(historicTaskInstance)) {
            return Response.error("根据请求的任务ID未查询到任务信息");
        }

        //核查下一个任务是否为活跃任务TO DO

        Task activeTask = taskService.createTaskQuery().active().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
        if (Objects.isNull(activeTask)) {
            return Response.error("根据请求的任务ID未查询到任务信息");
        }
        // 获取所有历史任务（按创建时间降序）
        List<HistoricTaskInstance> hisTaskList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(activeTask.getProcessInstanceId())
                .orderByTaskCreateTime()
                .desc()
                .list();

        if (!hisTaskList.get(1).getId().equals(historicTaskInstance.getId())) {
            return Response.error("已审批，当前状态不能回退！");
        }

        return backToPrevTask(activeTask, req.getComment());
    }

    /**
     * 驳回 / 回退
     *
     * @param task
     * @param comment
     * @return
     */
    protected Response<Boolean> backToPrevTask(Task task, String comment) {
        // 获取所有历史任务（按创建时间降序）
        List<HistoricTaskInstance> hisTaskList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByTaskCreateTime()
                .desc()
                .list();

        // 获取当前流程实例的所有的历史活动实例
        List<HistoricActivityInstance> hisActivityList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).list();

        // 如果历史活动实例为空 或者 数量才1个 说明不存在或者才刚开启流程
        if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
            return Response.success(false, "操作失败，无可回退的任务节点");
        }

        // 当前任务（通过历史任务按时间倒序，查询第1个）
        HistoricTaskInstance currentTask = hisTaskList.get(0);
        // 前一个任务（通过历史任务按时间倒序，查询第2个）
        HistoricTaskInstance lastTask = hisTaskList.get(1);

        //  当前活动（遍历当前流程实例的所有的历史活动实例, 根据当前任务的id（就是taskId）与历史活动实例的taskId相等, 找到对应的历史活动实例）
        HistoricActivityInstance currentActivity = hisActivityList.stream().filter(e -> currentTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);
        //  前一个活动（遍历当前流程实例的所有的历史活动实例, 根据前一个任务的id（就是taskId）与历史活动实例的taskId相等, 找到对应的历史活动实例）
        HistoricActivityInstance lastActivity = hisActivityList.stream().filter(e -> lastTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);

        // 使用repositoryService, 根据流程定义id获取 【bpmn模型对象】
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        //  获取前一个活动节点（从bpmn模型对象中，根据活动实例的活动id（就是标签的id属性）找到FlowNode。所以活动实例其实就是把taskId和活动id给结合起来了）
        FlowNode lastFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(lastActivity.getActivityId());
        //  获取当前活动节点（从bpmn模型对象中，根据活动实例的活动id（就是标签的id属性）找到FlowNode。所以活动实例其实就是把taskId和活动id给结合起来了）
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivity.getActivityId());

        //  临时保存当前活动的原始方向（这说明每1个FlowNode都包含1个SequenceFlow的outgoingFlows集合）
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode); // 当前flowNode
        newSequenceFlow.setTargetFlowElement(lastFlowNode);    // 上一个flowNode
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);

        //  完成当前任务（将会沿着新给定的方向流转到指定的节点）
        taskService.complete(task.getId());

        //  重新查询当前任务
        Task nextTask = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        if (null != nextTask) {
            // 因为现在已经退回到上一个节点了, 这里从历史任务中获取上1个节点的任务负责人, 设置到当前任务中
            taskService.setAssignee(nextTask.getId(), lastTask.getAssignee());
        }

        //  恢复原始方向
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
        return Response.success(true, "任务回退成功");
    }

    /**
     * 跳到最开始的任务节点（直接打回）
     *
     * @param task 当前任务
     */
    public Response<Boolean> backToStartTask(Task task, String comment) {

        String processInstanceId = task.getProcessInstanceId();

        //  获取所有历史任务（按创建时间升序）
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list();

        if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
            return Response.error("任务成功回退到开始");
        }

        //  第一个任务
        HistoricTaskInstance startTask = hisTaskList.get(0);
        //  当前任务
        HistoricTaskInstance currentTask = hisTaskList.get(hisTaskList.size() - 1);

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        //  获取第一个活动节点
        FlowNode startFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(startTask.getTaskDefinitionKey());
        //  获取当前活动节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentTask.getTaskDefinitionKey());

        //  临时保存当前活动的原始方向
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(startFlowNode);
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);

        //  完成当前任务
        taskService.complete(task.getId());

        //  重新查询当前任务
        Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (null != nextTask) {
            taskService.setAssignee(nextTask.getId(), startTask.getAssignee());
        }

        //  恢复原始方向
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);

        return Response.success(true, "任务成功回退到开始");
    }

    /**
     * 结束/终止任务
     *
     * @param req 任务请求对象
     */
    public Response<Boolean> terminateTask(TaskBaseReq req) {

        //  当前任务
        Task task = taskService.createTaskQuery().taskId(req.getTaskId()).singleResult();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        List<EndEvent> flowEndEventElements = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        FlowNode endFlowNode = flowEndEventElements.get(0);
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

        //  临时保存当前活动的原始方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        // 建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        List newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);

        // 当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), req.getComment());
        // 完成当前任务
        taskService.complete(task.getId());

        // 可以不用恢复原始方向，不影响其它的流程
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);

        return Response.success(true, "任务终止成功");
    }

}
